`timescale 1ns / 1ps
/************************************************************\
 **  Copyright (c) 2022-2023 Gonsin, Inc.
 **  All Right Reserved.
 **  Author: http://www.anlogic.com/
 **  Description: client_loopback
 **  Rev 1.0
\************************************************************/

//fsm type and signals
`define wait_sf 3'd0      // awaiting start of frame
`define bypass_sa1 3'd1   // bypassing first byte of source address
`define bypass_sa2 3'd2   // bypassing second byte of source address
`define bypass_sa3 3'd3   // bypassing third byte of source address
`define bypass_sa4 3'd4   // bypassing fourth byte of source address
`define bypass_sa5 3'd5   // bypassing fifth byte of source address
`define bypass_sa6 3'd6   // bypassing sixth byte of source address
`define pass_rof 3'd7     // transmitting remainder of data in frame

module client_loopback (
input rx_LLclock_i,         // Input CLK from TRIMAC Reciever
input rx_LLreset_i,         // Synchronous reset signal
input [7:0] rx8b_LLdata_i,  // Input data
input rx_LLsofN_i,          // Input start of frame
input rx_LLeofN_i,          // Input end of frame
input rx_LLsrc_rdyN_i,      // Input source ready
output reg [7:0] tx8b_LLdata_o,   // Modified output data
output tx_LLsofN_o,         // Output start of frame
output tx_LLeofN_o,         // Output end of frame
output tx_LLsrc_rdyN_o,     // Output source ready
input tx_LLdst_rdyN_i       // Input destination ready
);
  
//Signal declarations
reg sel_delay_path;        // controls mux in Process data_out_mux
reg enable_data_sr;        // clock enable for data shift register
wire [7:0] data8b_sr5_w;   // data after 6 cycle delay
reg [7:0] mux8b_out;       // data to output register

wire rx_enable_w;          // Internal enable signal

//state machine state variable
reg [2:0] control3b_fsm_sm; // holds state of control fsm

//6 stage shift register type and signals
reg [7:0] data8b_sr_content[0:5];  // holds contents of data sr

//7 stage shift register type and signals
reg eof_sr_content[0:5];    // holds contents of end of frame sr
reg sof_sr_content[0:5];    // holds contents of start of frame sr
reg rdy_sr_content[0:5];    // holds contents of source ready sr
reg eof_sr_content_out;     // holds contents of end of frame sr
reg sof_sr_content_out;     // holds contents of start of frame sr
reg rdy_sr_content_out;     // holds contents of source ready sr

integer i;    // index for sr processes

//----------------------------------------------------------------------------
//Process data_sr_p
//A six stage shift register to hold six bytes of incoming data.
//Clock enable signal enable_data_sr allows destination address to be stored
//in shift register when in bypass mode.
//----------------------------------------------------------------------------
always@(posedge rx_LLclock_i) begin
  if(enable_data_sr == 1'b1 && rx_enable_w == 1'b1) begin
    for(i=5; i >0; i=i-1) begin
      data8b_sr_content[i] <= data8b_sr_content [i-1];
    end
    
    data8b_sr_content[0] <= rx8b_LLdata_i;
  end
end   // data_sr_p

assign data8b_sr5_w = data8b_sr_content[5];

//----------------------------------------------------------------------------
//Process data_out_mux_p
//Selects data_out from the data shift register or from data_in, allowing
//destination address to be bypassed
//----------------------------------------------------------------------------
always@(rx8b_LLdata_i, data8b_sr5_w, sel_delay_path) begin
  if(sel_delay_path == 1'b1) begin
    mux8b_out = rx8b_LLdata_i;
  end
  else begin
    mux8b_out = data8b_sr5_w;
  end
end  //  data_out_mux_p

//----------------------------------------------------------------------------
//Process data_out_reg_p
//Registers data output from output mux
//----------------------------------------------------------------------------
always@(posedge rx_LLclock_i) begin 
  if(rx_enable_w == 1'b1) begin
    tx8b_LLdata_o <= mux8b_out;
  end
end  // data_out_reg_p

assign rx_enable_w = ~(tx_LLdst_rdyN_i);

//----------------------------------------------------------------------------
//Process data_sof_sr_p
//Delays start of frame by 7 clock cycles
//----------------------------------------------------------------------------
always@(posedge rx_LLclock_i) begin 
  if(rx_enable_w == 1'b1) begin
    for(i=5; i>0; i=i-1) begin
      sof_sr_content[i] <= sof_sr_content[i-1];
      sof_sr_content[0] <= !rx_LLsofN_i;
    end 
  end 
end   // data_sof_sr_p

always@(posedge rx_LLclock_i) begin
  if(rx_LLreset_i == 1) begin
    sof_sr_content_out <= 0;
  end 
  else if(rx_enable_w == 1'b1) begin
    sof_sr_content_out <= sof_sr_content[5];
  end 
end 

assign tx_LLsofN_o = !sof_sr_content_out;

//----------------------------------------------------------------------------
//Process data_eof_sr_p
//Delays end of frame by 7 clock cycles
//----------------------------------------------------------------------------
always@(posedge rx_LLclock_i) begin
  if(rx_enable_w == 1'b1) begin
    for(i=5; i>0; i=i-1) begin
      eof_sr_content[i] <= eof_sr_content[i-1];
    end
    eof_sr_content[0] <= !rx_LLeofN_i;
  end	
end // data_bad_sr_p

always@(posedge rx_LLclock_i) begin
  if(rx_LLreset_i == 1) begin
    eof_sr_content_out <= 0;
  end 
  else if(rx_enable_w == 1'b1) begin
    eof_sr_content_out <= eof_sr_content[5];
  end 
end 

assign tx_LLeofN_o = !eof_sr_content_out;

//----------------------------------------------------------------------------
//Process data_rdy_sr_p
//Delays source ready by 7 clock cycles
//----------------------------------------------------------------------------
always@(posedge rx_LLclock_i) begin
  if(rx_enable_w == 1'b1) begin
    for(i=5; i>0; i=i-1) begin
      rdy_sr_content[i] <= rdy_sr_content[i-1];
    end
    rdy_sr_content[0] <= !rx_LLsrc_rdyN_i;
  end	
end // data_rdy_sr_p

always@(posedge rx_LLclock_i)
begin
  if(rx_LLreset_i == 1) begin
    rdy_sr_content_out <= 0;
  end 
  else if(rx_enable_w == 1'b1) begin
    rdy_sr_content_out <= rdy_sr_content[5];
  end 
end 

assign tx_LLsrc_rdyN_o = !rdy_sr_content_out;

//----------------------------------------------------------------------------
//Process control_fsm_sync_p
//Synchronous update of next state of control_fsm
//----------------------------------------------------------------------------
always@(posedge rx_LLclock_i) begin
  if(rx_LLreset_i == 1) begin
    control3b_fsm_sm <= `wait_sf;
  end 
    
  else if(rx_enable_w == 1'b1) begin
    case(control3b_fsm_sm)
      `wait_sf:
        if(sof_sr_content[4] == 1'b1) begin
          control3b_fsm_sm <= `bypass_sa1;  // Start of frame detected
        end
        else begin
          control3b_fsm_sm <= `wait_sf;     // Continue to wait for sof
        end
      
      `bypass_sa1:
        if(!(sof_sr_content[4] == 1'b0 && eof_sr_content[4] == 1'b1)) begin
          control3b_fsm_sm <= `bypass_sa2;  // Pass next byte of source address
        end
        else begin
          control3b_fsm_sm <= `wait_sf;     // Frame ended, wait for next frame
        end
      
      `bypass_sa2:
        if(!(sof_sr_content[4] == 1'b0 && eof_sr_content[4] == 1'b1)) begin
          control3b_fsm_sm <= `bypass_sa3;  // Pass next byte of source address
        end
        else begin
          control3b_fsm_sm <= `wait_sf;     // Frame ended, wait for next frame
        end
          
      `bypass_sa3:
        if(!(sof_sr_content[4] == 1'b0 && eof_sr_content[4] == 1'b1)) begin
          control3b_fsm_sm <= `bypass_sa4;  // Pass next byte of source address
        end
        else begin
          control3b_fsm_sm <= `wait_sf;     // Frame ended, wait for next frame
        end
          
      `bypass_sa4:
        if(!(sof_sr_content[4] == 1'b0 && eof_sr_content[4] == 1'b1)) begin
          control3b_fsm_sm <= `bypass_sa5;  // Pass next byte of source address
        end
        else begin
          control3b_fsm_sm <= `wait_sf;     // Frame ended, wait for next frame
        end
          
      `bypass_sa5:
        if(!(sof_sr_content[4] == 1'b0 && eof_sr_content[4] == 1'b1)) begin
          control3b_fsm_sm <= `bypass_sa6;  // Pass next byte of source address
        end
        else begin
          control3b_fsm_sm <= `wait_sf;     // Frame ended, wait for next frame
        end
          
      `bypass_sa6:
        if(!(sof_sr_content[4] == 1'b0 && eof_sr_content[4] == 1'b1)) begin
          control3b_fsm_sm <= `pass_rof;    // Output remaining data in frame   
        end
        else begin
          control3b_fsm_sm <= `wait_sf;     // Frame ended, wait for next frame  
        end
          
      `pass_rof:
        if(!(sof_sr_content[4] == 1'b0 && eof_sr_content[4] == 1'b1)) begin
          control3b_fsm_sm <= `pass_rof;    // Output remaining data in frame   
        end
        else begin
          control3b_fsm_sm <= `wait_sf;     // Frame ended, wait for next frame   
        end
          
      default:
        control3b_fsm_sm <= `wait_sf;   
    endcase
  end
end // control_fsm_sync_p

//----------------------------------------------------------------------------
//Process control_fsm_comb_p
//Determines control signals from control_fsm state
//----------------------------------------------------------------------------
always@(control3b_fsm_sm) begin
  case (control3b_fsm_sm)
    `wait_sf: begin
      sel_delay_path = 1'b0;  // output data from data shift register
      enable_data_sr = 1'b1;  // enable data to be loaded into shift register
    end
      
    `bypass_sa1: begin
      sel_delay_path = 1'b1;  // output data directly from input
      enable_data_sr = 1'b0;  // hold current data in shift register
    end
      
    `bypass_sa2: begin
      sel_delay_path = 1'b1;  // output data directly from input
      enable_data_sr = 1'b0;  // hold current data in shift register
    end
      
    `bypass_sa3: begin
      sel_delay_path = 1'b1;  // output data directly from input
      enable_data_sr = 1'b0;  // hold current data in shift register
    end
      
    `bypass_sa4: begin
      sel_delay_path = 1'b1;  // output data directly from input
      enable_data_sr = 1'b0;  // hold current data in shift register
    end
      
    `bypass_sa5: begin
      sel_delay_path = 1'b1;  // output data directly from input
      enable_data_sr = 1'b0;  // hold current data in shift register
    end
      
    `bypass_sa6: begin 
      sel_delay_path = 1'b1;  // output data directly from input
      enable_data_sr = 1'b0;  // hold current data in shift register
    end
      
    `pass_rof: begin
      sel_delay_path = 1'b0;  // output data from data shift register
      enable_data_sr = 1'b1;  // enable data to be loaded into shift register
    end
      
    default: begin
      sel_delay_path = 1'b0;
      enable_data_sr = 1'b1;
    end
  endcase
end   // control_fsm_comb_p
   
endmodule